package cc.autoapi.pucong.auto.flow.provider.api.manager;


import cc.autoapi.pucong.auto.flow.core.flow.execNode.ai.send.AbstractAiSendSendTool;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.api.design.ApiNodeParamsVo;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.api.design.ApiNodeRelationVo;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.api.design.ApiNodeVo;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.entity.node.*;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.selfenum.NodeTypeEnum;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.task.AbstractTask;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.task.TaskFactory;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.utils.AiJsonUtils;
import cc.autoapi.pucong.auto.flow.core.flow.execNode.utils.FlowDesignUtils;
import cc.autoapi.pucong.auto.flow.core.root.properties.AutoFlowDesignConfiguration;
import cc.autoapi.pucong.auto.flow.persistence.po.properties.PropertiesInfoPo;
import cc.autoapi.pucong.auto.flow.persistence.repository.api.ApiNodeService;
import cc.autoapi.pucong.auto.flow.persistence.repository.properties.PropertiesInfoService;
import cc.autoapi.pucong.auto.flow.provider.api.entity.dto.ApiDesignAiDto;
import cc.autoapi.pucong.auto.flow.provider.api.entity.vo.ApiDesignAiVo;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author pucong
 * @description AI 设计编排
 * @create 2024-09-24
 */
@Service
@Slf4j
public class ApiDesignAiManager {

    @Resource
    private ApiNodeManager apiNodeManager;

    @Resource
    private ApiNodeService apiNodeService;

    @Resource
    private AutoFlowDesignConfiguration autoFlowDesignConfiguration;

    @Resource
    private AbstractAiSendSendTool aiSendSendTool;


    @Resource
    private PropertiesInfoService propertiesInfoService;


    private static final Map<String, JSONArray> aiMsgs = new HashMap<>(16);

    private static String prompt = " 你是一个流程编排大师。根据提供组件列表、画布中已存在的组件实例、已存在的组件实例关系和功能描述，调用工具完成流程编排。\n" +
            "                        \n" +
            "            你的任务是：\n" +
            "            1. 在画布中创建组件实例，并设置组件实例之间的关系和属性。\n" +
            "            2. 根据组件的规则，某些组件可以添加输入和输出参数，你需要为这些特殊组件添加输入和输出参数。\n" +
            "            3. 设置组件的输入值来源，可以是静态值、前面组件的输出参数、或全局参数。\n" +
            "            4. 使用工具构建清晰的规则和逻辑结构，确保每个组件实例至少有一个关系。\n" +
            "            5. 当调用工具返回执行失败时，分析原因并修正参数，再次调用工具。\n" +
            "                        \n" +
            "            流程编排的规则：\n" +
            "            1. 组件编码为“apiConfig”、“cronConfig”的组件是起始节点。\n" +
            "            2. 先执行起始组件，根据关系决定执行顺序。\n" +
            "            3. 当执行节点时，判断上一节点是否执行完成，上一节点都执行完成才能执行本节点。当前组件执行完成后，下一执行组件有多个时，异步同时执行下一节点。\n" +
            "            4. 节点之间的关系类型有：0：一般执行关系，1：catch执行关系，2：工具执行关系，3：true执行关系，4：false执行关系。\n" +
            "            5. 组件jsScript和python只做数据处理，复杂功能需交给java脚本组件。\n" +
            "            6. 输入输出参数的数据类型是数字，含义分别为，0：\"number\", 1：\"string\", 2：\"txt\", 3：\"Boolean\", 4：\"Date\", 5：\"List\", 6：\"Map\", 7：\"Object\"。\n" +
            "            7. 组件实例下的输入参数编码不能相同，输出参数编码也不能相同。\n" +
            "            8. 添加了实例组件后，需要添加关系，才能生成正确的流程图。\n" +
            "            9. 不能添加输入和输出参数的组件，不能调用添加输入和输出参数的工具。\n" +
            "            \n" +
            "            全局参数：\n" +
            "            {globalParams}\n" +
            "                        \n" +
            "            组件列表：\n" +
            "            {nodes}\n" +
            "                        \n" +
            "            画布中的组件：\n" +
            "            {nodeInstances}\n" +
            "                        \n" +
            "            画布中的组件关系：\n" +
            "            {nodeInsRelations}\n";

    public void removeChat(String chatId) {
        aiMsgs.remove(chatId);
    }

    public synchronized ApiDesignAiVo aiDesign(String chatId, ApiDesignAiDto dto) {
        JSONArray messages = aiMsgs.get(chatId);
        if (messages == null) {
            messages = new JSONArray();
            List<ApiNodeRelationVo> addRelateList = new ArrayList<>();
            List<ApiNodeVo> addNodeList = new ArrayList<>();
            List<ApiNodeParamsVo> apiNodeParamsVoList = new ArrayList<>();
            // 解析json
            FlowDesignUtils.dealJson2List(dto.getApiJson(),
                    addNodeList, addRelateList,
                    apiNodeParamsVoList);
            List<TaskNode> taskNodeList = apiNodeService.getTaskNodeList(addNodeList, apiNodeParamsVoList);
            List<TaskNodeRelation> taskNodeRelationList = apiNodeService.getTaskNodeRelationList(addRelateList);

            // 可用组件
            String system = prompt.replace("{nodes}", getNodes());
            system = system.replace("{globalParams}", getGlobalParams());
            system = system.replace("{nodeInstances}", getNodeInstances(taskNodeList));
            system = system.replace("{nodeInsRelations}", getNodeInsRelations(taskNodeRelationList));

            JSONObject system2 = new JSONObject();
            system2.put("role", "system");
            system2.put("content", system);
            messages.add(system2);

            aiMsgs.put(chatId, messages);
        }
        JSONObject userMsg = new JSONObject();
        userMsg.put("role", "user");
        userMsg.put("content", dto.getQuestion());
        messages.add(userMsg);

        JSONArray tools = AiJsonUtils.getToolsJon();
        if (tools != null) {
            aiSendSendTool.initTools(tools);
        }
        JSONArray callTools = dto.getCallTools();
        if (callTools != null) {
            for (Object toolCall : callTools) {
                JSONObject toolCallJson = (JSONObject) toolCall;
                String id = toolCallJson.getString("id");
                JSONObject toolResult = toolCallJson.getJSONObject("toolResult");
                String toolCallName = toolCallJson.getJSONObject("function").getString("name");
                // 设置结果
                JSONObject msg = new JSONObject();
                msg.put("role", "tool");
                msg.put("tool_call_id", id);
                msg.put("name", toolCallName);
                msg.put("content", JSON.toJSONString(toolResult));
                messages.add(msg);
            }
        }
        // 发送ai
        JSONObject message = aiSendSendTool.sendToolsToAi(messages);
        if (message == null) {
            ApiDesignAiVo vo = new ApiDesignAiVo();
            vo.setFinish(true);
            return vo;
        }
        // 记录
        messages.add(message);
        JSONArray toolCalls = message.getJSONArray("tool_calls");
        if (toolCalls == null) {
            log.debug("发送");
            log.debug(messages.toJSONString(JSONWriter.Feature.PrettyFormat));
            log.debug("回答");
            log.debug(message.toJSONString(JSONWriter.Feature.PrettyFormat));
            ApiDesignAiVo vo = new ApiDesignAiVo();
            vo.setFinish(true);
            return vo;
        }
        // 返回数据
        ApiDesignAiVo vo = new ApiDesignAiVo();
        vo.setCallTools(toolCalls);
        vo.setFinish(false);
        return vo;
    }

    private String getGlobalParams() {
        List<PropertiesInfoPo> list = propertiesInfoService.list();
        JSONArray nodes = new JSONArray();
        for (PropertiesInfoPo po : list) {
            JSONObject node = new JSONObject();
            node.put("id", po.getUuid());
            node.put("参数编码", po.getPropertiesCode());
            node.put("描述", po.getPropertiesDesc());
            nodes.add(node);
        }
        return nodes.toJSONString();
    }


    private static String getFirstNodeDesc(List<ApiNodeVo> addNodeList) {
        for (ApiNodeVo apiNodeVo : addNodeList) {
            String nodeType = apiNodeVo.getNodeType();
            if (nodeType.equals(NodeTypeEnum.API_CONFIG.getKey()) || nodeType.equals(NodeTypeEnum.CRON_CONFIG.getKey())) {
                return apiNodeVo.getNodeDesc();
            }
        }
        return "";
    }

    private static String getNodes() {
        JSONArray nodes = new JSONArray();
        NodeTypeEnum[] values = NodeTypeEnum.values();
        for (NodeTypeEnum value : values) {
            if (value == NodeTypeEnum.API_CONFIG || value == NodeTypeEnum.CRON_CONFIG) {
                continue;
            }
            AbstractTask task = TaskFactory.getTask(value);
            JSONObject node = getNode(value.getKey(), task);
            nodes.add(node);
        }

        Set<String> taskMapKey = TaskFactory.getTaskMapKey();
        for (String s : taskMapKey) {
            AbstractTask task = TaskFactory.getTask(s);
            JSONObject node = getNode(s, task);
            nodes.add(node);
        }
        return nodes.toJSONString();
    }

    private static JSONObject getNode(String nodeCode, AbstractTask task) {
        JSONObject base1 = new JSONObject();
        base1.put("组件名称", task.getTaskName());
        base1.put("组件编码", nodeCode);
        base1.put("描述", task.getTaskDesc());
        base1.put("是否可以添加输入和输出参数", task.getTaskParamInAuto());
        List<NodeParamIn> propertyParamsIns = task.getPropertyParamsIns();
        JSONArray paramsInList = new JSONArray();
        for (NodeParamIn nodeParamIn : propertyParamsIns) {
            JSONObject apiNodeParamsInVo = new JSONObject();
            apiNodeParamsInVo.put("参数编码", nodeParamIn.getParamCode());
            apiNodeParamsInVo.put("数据类型", nodeParamIn.getDateType().getType());
            apiNodeParamsInVo.put("是否必填", nodeParamIn.getNeedValue());
            apiNodeParamsInVo.put("描述", nodeParamIn.getDesc());
            paramsInList.add(apiNodeParamsInVo);
        }
        base1.put("输入参数列表", paramsInList);
        JSONArray paramsOutList = new JSONArray();
        List<NodeParamOut> propertyParamsOuts = task.getPropertyParamsOuts();
        for (NodeParamOut nodeParamOut : propertyParamsOuts) {
            JSONObject apiNodeParamsOutVo = new JSONObject();
            apiNodeParamsOutVo.put("参数编码", nodeParamOut.getParamCode());
            apiNodeParamsOutVo.put("数据类型", nodeParamOut.getDateType().getType());
            apiNodeParamsOutVo.put("描述", nodeParamOut.getDesc());
            paramsOutList.add(apiNodeParamsOutVo);
        }
        base1.put("输出参数列表", paramsOutList);
        return base1;
    }


    private static String getNodeInstances(List<TaskNode> taskNodeList) {
        JSONArray nodes = new JSONArray();
        for (TaskNode taskNode : taskNodeList) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("实例组件id", taskNode.getUuid());
            jsonObject.put("组件名称", taskNode.getNodeName());
            jsonObject.put("组件编码", taskNode.getNodeType());
            jsonObject.put("描述", taskNode.getNodeDesc());

            JSONArray paramsInList = new JSONArray();
            List<TaskNodeParamIn> taskNodeParamInList = taskNode.getTaskNodeParamInList();
            if (taskNodeParamInList != null) {
                for (TaskNodeParamIn in : taskNodeParamInList) {
                    JSONObject apiNodeParamsInVo = new JSONObject();
                    apiNodeParamsInVo.put("参数编码", in.getParamCode());
                    apiNodeParamsInVo.put("数据类型", in.getDateType().getName());
                    apiNodeParamsInVo.put("是否必填", in.getNeedValue());
                    apiNodeParamsInVo.put("描述", in.getParamDesc());
                    paramsInList.add(apiNodeParamsInVo);
                }
            }
            JSONArray paramsOutList = new JSONArray();
            List<TaskNodeParamOut> taskNodeParamOutList = taskNode.getTaskNodeParamOutList();
            if (taskNodeParamOutList != null) {
                for (TaskNodeParamOut out : taskNodeParamOutList) {
                    JSONObject apiNodeParamsOutVo = new JSONObject();
                    apiNodeParamsOutVo.put("参数编码", out.getParamCode());
                    apiNodeParamsOutVo.put("数据类型", out.getDateType().getName());
                    apiNodeParamsOutVo.put("描述", out.getParamDesc());
                    paramsOutList.add(apiNodeParamsOutVo);
                }
            }
            jsonObject.put("输入参数列表", paramsInList.toJSONString());
            jsonObject.put("输出参数列表", paramsOutList.toJSONString());
            nodes.add(jsonObject);
        }
        return nodes.toJSONString();
    }


    private static String getNodeInsRelations(List<TaskNodeRelation> taskNodeRelationList) {
        JSONArray relations = new JSONArray();
        for (TaskNodeRelation nodeRelation : taskNodeRelationList) {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("来源实例组件id", nodeRelation.getNodeFromId());
            jsonObject.put("目标实例组件id", nodeRelation.getNodeToId());
            jsonObject.put("关系类型", nodeRelation.getRelationType());
            relations.add(jsonObject);
        }
        return relations.toJSONString();
    }
}
